package com.gzsxy.esjy.sign.async;


import com.gzsxy.esjy.sign.exception.SignException;
import com.gzsxy.esjy.sign.socket.AsyncWsCallBack;
import com.gzsxy.esjy.sign.socket.WsClientMessageHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.net.URI;
import java.net.URISyntaxException;

/**
 * @author zhangxiaolong
 * @since 2022/7/15
 */
@Getter
@Setter
@Slf4j
public class AsyncWsClient {

    /**
     * 签名任务ID,发起一次签名相当一次任务
     */
    private String taskId;

    private Integer devType;

    /**
     * 请求ID,
     */
    private String requestId;

    /**
     * ip
     */
    private String ip;

    /**
     * 端口
     */
    private int port;

    /**
     * webSocket地址
     */
    private String wsUrl;
    /**
     * 读空闲  心跳
     */
    private long readHeart = 60;
    /**
     * 写空闲 心跳
     */
    private long writeHeart = 60;
    /**
     * 读写空闲 心跳
     */
    private long bothHeart = 600;
    /**
     * 连接通道
     */
    private Channel channel;

    private ChannelFuture channelFuture;
    /**
     * 异步回调接口
     */
    private AsyncWsCallBack asyncWsCallBack;
    /**
     * 启动器
     */
    private Bootstrap bootstrap;

    /**
     * 构造器1
     *
     * @param signId          业务标识
     * @param ip              IP
     * @param port            端口
     * @param wsUrl           wsl连接URL
     * @param asyncWsCallBack 消息处理回调
     * @param bootstrap       连接器
     */
    public AsyncWsClient(String signId, String ip, int port, String wsUrl, AsyncWsCallBack asyncWsCallBack, Bootstrap bootstrap) {
        this.taskId = signId;
        this.ip = ip;
        this.port = port;
        this.wsUrl = wsUrl;
        this.asyncWsCallBack = asyncWsCallBack;
        this.bootstrap = bootstrap;
    }

    /**
     * 连接设备
     */
    public void connect() {
        this.channel = null;
        URI wsUri = null;
        try {
            wsUri = new URI(wsUrl);
        } catch (URISyntaxException e) {
            log.error(e.getMessage(), e);
        }

        // 初始化ws握手客户端
        WebSocketClientHandshaker webSocketClientHandshaker = WebSocketClientHandshakerFactory
                .newHandshaker(wsUri, WebSocketVersion.V13, null, true,
                        new DefaultHttpHeaders(), 100 * 1024 * 1024);

        // 初始化客户端请求处理器
        WsClientMessageHandler handler = new WsClientMessageHandler(webSocketClientHandshaker, this);
        //设置处理器handler
        this.bootstrap.handler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel ch) {
                ChannelPipeline pipeline = ch.pipeline();
//                pipeline.addLast("ping", new IdleStateHandler(readHeart, writeHeart, bothHeart, TimeUnit.SECONDS));
                pipeline.addLast(new HttpClientCodec());
                pipeline.addLast(new HttpObjectAggregator(65536));
                pipeline.addLast(handler);
            }
        });

        // 连接服务端
        try {
            this.channelFuture = bootstrap.connect(ip, port).sync();

            // 每10毫秒判断一次
            int interval = 10;
            try {
                int connectSignTimeout = 5000;
                for (int i = 1, count = connectSignTimeout / interval; i <= count; i++) {
                    if (channel != null && channel.isOpen() && channel.isActive() && channel.isWritable()) {
                        //连接上返回
                        return;
                    }
                    Thread.sleep(interval);
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }

            disconnect();
            throw new SignException("请检查签字板服务是否启动");
        } catch (Exception e) {
            log.error("连接websocket失败：{}", e.getMessage());
            disconnect();
            throw new SignException("请检查签字板服务是否启动");
        }
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        try {
            if(null != channel && channel.isActive()){
                channel.close();
            }

            if (null != channelFuture) {
                Channel channel = channelFuture.channel();
                if (null != channel) {
                    channel.close();
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}
